#include "ar_app.h"
#include <system/platform.h>
#include <graphics/sprite_renderer.h>
#include <graphics/texture.h>
#include <graphics/mesh.h>
#include <graphics/primitive.h>
#include <assets/png_loader.h>
#include <graphics/image_data.h>
#include <graphics/font.h>
#include <input/touch_input_manager.h>
#include <maths/vector2.h>
#include <input/sony_controller_input_manager.h>
#include <maths/math_utils.h>
#include <graphics/renderer_3d.h>
#include <graphics/render_target.h>
#include <graphics/vita/texture_vita.h>

#include <sony_sample_framework.h>
#include <sony_tracking.h>


void *graphicsAlloc(SceKernelMemBlockType type, uint32_t size, uint32_t alignment, uint32_t attribs, SceUID *uid);
void graphicsFree(SceUID uid);

ARApp::ARApp(abfw::Platform& platform) :
	Application(platform),
	mSpriteRenderer(NULL),
	mFont(NULL),
	mRenderer3D(NULL),
	mRestart(false)
{
}

void ARApp::Init()
{
	
	mSpriteRenderer = platform_.CreateSpriteRenderer();
	mRenderer3D = platform_.CreateRenderer3D();

	InitFont();

	//-- set up orthographic projection matrix
	mProj2D.OrthographicFrustumVita(-1, 1, -1 , 1, -1, 1);
	
	//-- initialise sony framework
	sampleInitialize();
	smartInitialize();

	Meshes::InitPlaneMesh(platform_);

	//-- Calculate aspect ratios
	mCameraImageAspectRatio = 640.0f / 480.0f;
	mDisplayAspectRatio = 960.0f / 544.0f;
	mVecticleScaleFactor = mDisplayAspectRatio / mCameraImageAspectRatio;

	//-- calculate final 3d projection matrix
	abfw::Matrix44 tempProj;
	tempProj.PerspectiveFovVita(SCE_SMART_IMAGE_FOV, mCameraImageAspectRatio, 0.01f, 1000.0f);

	//-- scaling matrix
	abfw::Matrix44 scale_matrix;
	scale_matrix.Scale(abfw::Vector3(1.0f, mVecticleScaleFactor, 1.0f));

	mProj3D = tempProj * scale_matrix;

	mView.SetIdentity();

	//-- Set up camera sprite
	mCameraSprite.set_position(abfw::Vector3(0.0f,0.0f,1.0f));
	mCameraSprite.set_width(2.0f);
	mCameraSprite.set_height(2.0f * mVecticleScaleFactor);
	mCameraSprite.set_texture(&mCameraSpriteTexture);

	//-- Initialise marker lost and menu states
	mLostState = new MarkerLostState(platform_);
	mMenuState = new MenuState(platform_);
	mGameOverState = new GameOverState(platform_);

	//-- First state is menu
	mGameState = mMenuState;

	//-- Store the default ambient light colour 
	mDefaultColour = mRenderer3D->default_shader_data().ambient_light_colour();

	//-- Reset marker tracking
	AppData* dat = sampleUpdateBegin();
	smartTrackingReset();
	sampleUpdateEnd(dat);
	
}

void ARApp::CleanUp()
{
	smartRelease();
	sampleRelease();

	CleanUpFont();

	//-- delete allocated memory
	delete mSpriteRenderer;
	mSpriteRenderer = NULL;

	delete mRenderer3D;
	mRenderer3D = NULL;

	//-- clean up states
	delete mGameState;
	delete mPlayState;
	delete mGameOverState;
	delete mLostState;
	delete mMenuState;
}

bool ARApp::Update(float frame_time)
{
	//-- All game states can access FPS
	mFps = 1.0f / frame_time;
	mGameState->SetFPS(mFps);

	//-- Initialise marker transform to identity
	abfw::Matrix44 marker_transform;
	marker_transform.SetIdentity();

	AppData* dat = sampleUpdateBegin();

	//-- Use the tracking library to try and find markers
	smartUpdate(dat->currentImage);

	//-- Update all marker transforms
	for (int i = 0; i < 6; i++)
	{
		//-- If the current marker is found
		if(sampleIsMarkerFound(i))
		{			
			//-- Get its transform
			sampleGetTransform(i, &marker_transform);

			//-- Set the transform and card found boolean
			mGameState->SetARCardTransform(i, marker_transform);
			mGameState->ArCardFound(i,true);
		}
		else
		{
			//-- current marker not found
			mGameState->ArCardFound(i,false);
		}
	}

	//-- update the current state, returning the new state
	switch(mGameState->Update())
	{
	case GameStates::LOADSTATE:
		mPlayState = new PlayState(platform_);
		break;

	case GameStates::PLAYSTATE:
		mGameState = mPlayState;
		break;

	case GameStates::MARKERLOSTSTATE:
		mGameState = mLostState;
		break;

	case GameStates::MENUSTATE:
		//-- restart the game if coming from the game over state
		if (mRestart == true)
		{
			delete mPlayState;
			delete mMenuState;
			mMenuState = new MenuState(platform_);
			mRestart = false;
		}
		mGameState->CorrectCamera();
		mGameState = mMenuState;
		break;

	case GameStates::GAMEOVERSTATE:
		mGameOverState->SetPlayerScore(mPlayState->GetPlayerScore());
		mGameState = mGameOverState;
		
		mRestart = true;
		break;

	}

	sampleUpdateEnd(dat);

	return true;
	
}


void ARApp::Render()
{
	AppData* dat = sampleRenderBegin();

	//
	//-- Render the camera feed
	//

	//-- Set projection matrix
	mSpriteRenderer->set_projection_matrix(mProj2D);

	mSpriteRenderer->Begin(true);

	//-- Draw camera feed
	//-- Check there is data present for the camera image before trying to draw it
	if(dat->currentImage && mGameState != mMenuState 
							&& mGameState != mGameOverState)
	{
		mCameraSpriteTexture.set_texture(dat->currentImage->tex_yuv);
		mSpriteRenderer->DrawSprite(mCameraSprite);
	}
	RenderOverlay();

	mSpriteRenderer->End();

	//
	//-- Render 3D scene
	//

	//-- Set view and projection
	mRenderer3D->set_projection_matrix(mProj3D);
	mRenderer3D->set_view_matrix(mView);

	//-- Begin rendering 3D meshes
	mRenderer3D->Begin(false);

	//-- Draw everything
	mGameState->Draw3D(mRenderer3D, mDefaultColour);

	mRenderer3D->End();

	sampleRenderEnd();
}

void ARApp::InitFont()
{
	mFont = new abfw::Font();
	mFont->Load("comic_sans", platform_);
}

void ARApp::RenderOverlay()
{
	//
	//-- Render 2d hud on top
	//
	abfw::Matrix44 proj_matrix2d;
	proj_matrix2d.OrthographicFrustumVita(0.0f, platform_.width(), 0.0f, platform_.height(), -1.0f, 1.0f);
	mSpriteRenderer->set_projection_matrix(proj_matrix2d);
	mSpriteRenderer->Begin(false);

	if(mFont)
	{
		mGameState->Draw2D(mSpriteRenderer,proj_matrix2d);
	}

	mSpriteRenderer->End();
}

void ARApp::CleanUpFont()
{
	delete mFont;
	mFont = NULL;
}

